package jblip;

import jblip.resources.ResourcesFactory;

import java.io.File;
import java.io.InputStream;

import jblip.resources.Media;
import jblip.resources.Shortlink;
import jblip.resources.Subscription;
import jblip.resources.Update;
import jblip.resources.User;
import jblip.resources.UserPicture;

/**
 * The core system client class.
 * 
 * <p>
 * This is the class you should use as a base class in communication between
 * your application and a <i>Blip</i> system. This class provides just an
 * abstract interface, so you should either use default implementation provided
 * by JBlip library or create your own. It's suggested that you should begin
 * writing your implementation with writing your own resource and/or client
 * factories.
 * </p>
 * 
 * <p>
 * Most of the functions interacting with the Blip servers might fail from a
 * variety of reasons. If they do, the subsequent calls to the
 * {@link BlipClient#getLastError()} method should return an
 * {@link ErrorMessage} instance with details on the reason of failure. If an
 * error occurs within a method returning a value, that methods may return
 * <code>null</code> (although it's not guaranteed).
 * </p>
 * 
 * @author Krzysztof Sroka
 * @since 0.1
 * @see ResourcesFactory
 */
public abstract class BlipClient {
  /** Blip API provider host */
  protected final String host_;

  /** Blip user's login name */
  protected final String user_;

  /** Blip user's password */
  protected final String pass_;

  /** Resource factory currently used by this client. */
  protected ResourcesFactory resource_factory;

  /**
   * Creates a new instance of <i>Blip</i> system client.
   * 
   * @param blip_host
   *          Blip API provider host <i>(not the HTTP front-end provider)</i>
   * @param blip_user
   *          User's login name. If will be used in all cases when the
   *          authorization is required or forced. Set it to "" (empty string)
   *          if you don't want to authorize.
   * @param blip_pass
   *          User's password, used in the same manner as <code>blip_user</code>
   *          .
   */
  protected BlipClient(String blip_host, String blip_user, String blip_pass) {
    if (blip_host == null) {
      throw new NullPointerException("Host is null");
    }

    if (blip_user == null) {
      throw new NullPointerException("Username is null");
    }

    if (blip_pass == null) {
      throw new NullPointerException("Password is null");
    }

    this.host_ = blip_host;
    this.user_ = blip_user;
    this.pass_ = blip_pass;
  }

  /**
   * Returns user name associated with this instance.
   * 
   * @return user name associated with this instance.
   */
  public String getLoggedUserName() {
    return this.user_;
  }

  /**
   * Returns user password associated with this instance.
   * 
   * @return user password associated with this instance.
   */
  public String getLoggedUserPassword() {
    return this.pass_;
  }

  /**
   * Returns the host name of the blip server used.
   * 
   * @return the host name of the blip server used.
   */
  public String getHost() {
    return this.host_;
  }

  /**
   * Returns the currently used resource factory.
   * 
   * @return the currently used resource factory, or <code>null</code> if it's
   *         not set.
   */
  public final ResourcesFactory getResourcesFactory() {
    return resource_factory;
  }

  /**
   * Sets the resource factory to use.
   * 
   * @param factory
   *          Resource factory to use. Can't be null and it's supported API must
   *          match this client's supported API.
   * 
   * @see #getSupportedAPI()
   * @throws IllegalArgumentException
   *           if the given factory's supported API doesn't match API supported
   *           by this client.
   */
  public final void setResourcesFactory(ResourcesFactory factory) {
    if (factory == null) {
      throw new NullPointerException("Factory parameter is null.");
    } else {
      this.resource_factory = factory;
    }
  }

  /**
   * Gets a single message from the system.
   * 
   * @param id
   *          Requested message's id
   * @return Update object containing requested message or <code>null</code> in
   *         case of error.
   */
  public abstract Update getMessage(int id);

  /**
   * Gets specified user's avatar (profile picture).
   * 
   * @param user
   *          User name.
   * @return {@link UserPicture} instance containing URL to the original profile
   *         picture, or <code>null</code> if such doesn't exist.
   */
  public abstract UserPicture getAvatar(String user);

  /**
   * Gets specified user's avatar (profile picture) by user's ID.
   * 
   * @param user_id
   *          User's ID.
   * @return {@link UserPicture} instance containing URL to the original profile
   *         picture, or <code>null</code> if such doesn't exist.
   */
  public abstract UserPicture getAvatar(Integer user_id);

  /**
   * Deletes logged in user's avatar (profile picture).
   */
  public abstract void deleteAvatar();

  /**
   * Gets specified user's background picture.
   * 
   * @param user
   *          User name.
   * @return {@link Media} instance containing URL to the original background
   *         picture, or <code>null</code> if such doesn't exist.
   */
  public abstract Media getBackground(String user);

  /**
   * Gets specified user's background picture by user's ID.
   * 
   * @param user_id
   *          User's ID.
   * @return {@link Media} instance containing URL to the original background
   *         picture, or <code>null</code> if such doesn't exist.
   */
  public abstract Media getBackground(Integer user_id);

  /**
   * Deletes logged in user's background picture.
   */
  public abstract void deleteBackground();

  /**
   * Gets the enumeration of pictures associated with a given update.
   * 
   * <p>
   * The enumeration elements should be returned preserving the order obtained
   * from the Blip system.
   * </p>
   * 
   * @param update
   *          ID of the update containing pictures.
   * @return Enumeration of {@link Media} resources, or <code>null</code> if the
   *         update contains no images or if there was an error.
   */
  public abstract Iterable<? extends Media> getPictures(Integer update);

  /**
   * Gets the audio recording associated with a given update.
   * 
   * @param update
   *          ID of the update containing the recording.
   * @return Media resource representing an audio recording, or
   *         <code>null</code> if the update doesn't contain any recording or if
   *         there was an error.
   */
  public abstract Media getRecording(Integer update);

  /**
   * Gets the video recording associated with a given update.
   * 
   * <p>
   * NOTE: As of Blip API 0.02, a video recording is associated with an update
   * only if it was sent via a MMS message. In the HTML interface of the Blip
   * system it is suggested that embedding a link to a movie on the YouTube
   * service in a message associates that video with the message, but it is not
   * so in terms of this query.
   * </p>
   * 
   * @param update
   *          ID of the update containing the recording.
   * @return Media resource representing a video recording, or <code>null</code>
   *         if the update doesn't contain any recording or if there was an
   *         error.
   */
  public abstract Media getMovie(Integer update);

  /**
   * Posts a message to the system.
   * 
   * @param body
   *          Message's body.
   */
  public abstract void postMessage(String body);

  /**
   * Sends a message to the specified user.
   * 
   * <p>
   * This method is preferred over {@link #postMessage(String)} when sending a
   * directed message. It has the same effect as the &quot;>user:&quot; prefix
   * in the messages body.
   * </p>
   * 
   * @param body
   *          Message's body.
   * @param recipient
   *          Message's recipient user name.
   */
  public abstract void postMessage(String body, String recipient);

  /**
   * Sends a message with picture from the given stream.
   * 
   * @param body
   *          Message's body.
   * @param picture
   *          Stream from which the attached picture is obtained. Once the
   *          picture is sent, the stream is closed.
   */
  public abstract void postMessage(String body, InputStream picture);

  /**
   * Sends a message to the specified user with picture from the given stream.
   * 
   * <p>
   * This method is preferred over {@link #postMessage(String, InputStream)}
   * when sending a directed message with a picture. It has the same effect as
   * the &quot;>user:&quot; prefix in the messages body.
   * </p>
   * 
   * @param body
   *          Message's body.
   * @param recipient
   *          Message's recipient.
   * @param picture
   *          Stream from which the attached picture is obtained. Once the
   *          picture is sent, the stream is closed.
   */
  public abstract void postMessage(String body, String recipient,
      InputStream picture);

  /**
   * Sends a message with picture from the given file.
   * 
   * @param body
   *          Message's body.
   * @param picture
   *          File from which the attached picture is obtained.
   */
  public abstract void postMessage(String body, File picture);

  /**
   * Sends a message to the specified user with picture from the given file.
   * 
   * <p>
   * This method is preferred over {@link #postMessage(String, File)} when
   * sending a directed message with a picture. It has the same effect as the
   * &quot;>user:&quot; prefix in the messages body.
   * </p>
   * 
   * @param body
   *          Message's body.
   * @param recipient
   *          Message's recipient.
   * @param picture
   *          File from which the attached picture is obtained.
   */
  public abstract void postMessage(String body, String recipient, File picture);

  /**
   * Sends a private message to the specified user.
   * 
   * <p>
   * This method is preferred over {@link #postMessage(String, String)} when
   * sending a private message. It has the same effect as the
   * &quot;&gt;&gt;user:&quot; prefix in the message's body.
   * </p>
   * 
   * @param body
   *          Message's body.
   * @param recipient
   *          Message's recipient.
   */
  public abstract void postPrivateMessage(String body, String recipient);

  /**
   * Sends a message to the specified user with picture from the given file.
   * 
   * <p>
   * This method is preferred over {@link #postMessage(String, InputStream)}
   * when sending a private message with a picture. It has the same effect as
   * the &quot;&gt;&gt;user:&quot; prefix in the messages body.
   * </p>
   * 
   * @param body
   *          Message's body.
   * @param recipient
   *          Message's recipient.
   * @param picture
   *          Stream from which the attached picture is obtained. Once the
   *          picture is sent, the stream is closed.
   */
  public abstract void postPrivateMessage(String body, String recipient,
      InputStream picture);

  /**
   * Sends a message to the specified user with picture from the given file.
   * 
   * <p>
   * This method is preferred over {@link #postMessage(String, File)} when
   * sending a private message with a picture. It has the same effect as the
   * &quot;&gt;&gt;user:&quot; prefix in the messages body.
   * </p>
   * 
   * @param body
   *          Message's body.
   * @param recipient
   *          Message's recipient.
   * @param picture
   *          File from which the attached picture is obtained.
   */
  public abstract void postPrivateMessage(String body, String recipient,
      File picture);

  /**
   * Deletes a message from the system.
   * 
   * @param id
   *          ID of a message to be deleted.
   */
  public abstract void deleteMessage(Integer id);

  /**
   * Polls system for messages sent by the logged in user.
   * 
   * <p>
   * Polling in various {@link BlipClient} methods is understood as fetching a
   * list of messages from the server, given some parameters and filters on the
   * returned list. Most of the polls receive parameters <i>offset</i>,
   * <i>limit</i> and <i>since</i>, determining which messages to return. If not
   * given (set to <code>null</code>), these parameters default to a built-in
   * server-side parameters. You should refer to the Blip API specification for
   * the exact meaning of parameters and differences between various polls.
   * </p>
   * 
   * <p>
   * <i><strong>WARNING</strong>: do not use polling functionality too often, as
   * it will result in the error response from the server.</i>
   * </p>
   * 
   * @param since
   *          ID number of last message received.
   * @param limit
   *          Limit on number of messages fetched.
   * @param offset
   *          Offset in list of messages returned by server.
   * 
   * @return Enumeration of <code>Update</code> resources or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Update> poll(Integer since,
      Integer limit, Integer offset);

  /**
   * Polls system for messages sent by the specified user.
   * 
   * @see #poll(Integer, Integer, Integer)
   * @param user
   *          Requested updates author name.
   * @param since
   *          ID number of last message received.
   * @param limit
   *          Limit on number of messages fetched.
   * @param offset
   *          Offset in list of messages returned by server.
   * @return Enumeration of <code>Update</code> resources or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Update> poll(String user,
      Integer since, Integer limit, Integer offset);

  /**
   * Polls system for all public messages sent by all users.
   * 
   * @see #poll(Integer, Integer, Integer)
   * @param since
   *          ID number of last message received.
   * @param limit
   *          Limit on number of messages fetched.
   * @param offset
   *          Offset in list of messages returned by server.
   * @return Enumeration of <code>Update</code> resources or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Update> pollAll(Integer since,
      Integer limit, Integer offset);

  /**
   * Polls system for messages in the <i>Bliposhpere</i>.
   * 
   * @param since
   *          ID number of last message received.
   * @param limit
   *          Limit on number of messages fetched.
   * @param offset
   *          Offset in list of messages returned by server.
   * 
   * @see #poll(Integer, Integer, Integer)
   * @return Enumeration of <code>Update</code> resources or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Update> pollBliposhere(Integer since,
      Integer limit, Integer offset);

  /**
   * Polls system for messages containing specified tag.
   * 
   * <p>
   * Tag is a word starting with a <code>#</code> sign, i.e. the word
   * <i>#blip</i> in a message tags that message with a <i>blip</i> tag.
   * </p>
   * 
   * @see #poll(Integer, Integer, Integer)
   * @param tag
   *          Requested tag (without the <code>#</code> prefix).
   * @param since
   *          ID number of last message received.
   * @param limit
   *          Limit on number of messages fetched.
   * @param offset
   *          Offset in list of messages returned by server.
   * @return Enumeration of <code>Update</code> resources or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Update> pollTag(String tag,
      Integer since, Integer limit, Integer offset);

  /**
   * Polls system for messages from the currently logged in user's dashboard.
   * 
   * <p>
   * User's <i>dashboard</i> contains:
   * <ul>
   * <li>messages sent by the dashboard's owner,</li>
   * <li>messages sent to the dashboard's owner, and</li>
   * <li>status messages sent by any user tracked by the dashboard's owner.</li>
   * </ul>
   * </p>
   * 
   * 
   * @see #poll(Integer, Integer, Integer)
   * @param since
   *          ID number of last message received.
   * @param limit
   *          Limit on number of messages fetched.
   * @param offset
   *          Offset in list of messages returned by server.
   * @return Enumeration of <code>Update</code> resources or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Update> pollDashboard(Integer since,
      Integer limit, Integer offset);

  /**
   * Polls system for messages from the specified user's dashboard.
   * 
   * <p>
   * User's <i>dashboard</i> contains:
   * <ul>
   * <li>messages sent by the dashboard's owner,</li>
   * <li>messages sent to the dashboard's owner, and</li>
   * <li>status messages sent by any user tracked by the dashboard's owner.</li>
   * </ul>
   * </p>
   * 
   * @see #poll(Integer, Integer, Integer)
   * @param user
   *          User name of dashboard's owner.
   * @param since
   *          ID number of last message received.
   * @param limit
   *          Limit on number of messages fetched.
   * @param offset
   *          Offset in list of messages returned by server.
   * @return Enumeration of <code>Update</code> resources or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Update> pollDashboard(String user,
      Integer since, Integer limit, Integer offset);

  /**
   * Gets the list of most recently submitted pictures.
   * 
   * @param since
   *          ID number of last picture received.
   * @param limit
   *          Limit on number of resources fetched.
   * @param offset
   *          Offset in list of resources returned by server.
   * @return Enumeration of {@link Media} resources or <code>null</code> in case
   *         of error.
   */
  public abstract Iterable<? extends Media> pollPictures(Integer since,
      Integer limit, Integer offset);

  /**
   * Gets the list of most recently created shortlinks.
   * 
   * @see #poll(Integer, Integer, Integer)
   * @param since
   *          ID number of last shortlink received.
   * @param limit
   *          Limit on number of resources fetched.
   * @param offset
   *          Offset in list of resources returned by server.
   * @return Enumeration of {@link Shortlink} resources or <code>null</code> in
   *         case of error.
   */
  public abstract Iterable<? extends Shortlink> pollShortlinks(
      Integer since, Integer limit, Integer offset);

  /**
   * Gets the list of the given user's subscriptions.
   * 
   * @param user
   *          Name of the user whose subscriptions are to be obtained, or
   *          <code>null</code> if asking for currently authenticated user
   *          subscriptions.
   * @return Enumeration of {@link Subscription} instances or <code>null</code>
   *         in case of error.
   */
  public abstract Iterable<? extends Subscription> getSubscriptions(
      String user);

  /**
   * Gets the list of subscriptions <i>to</i> the given user.
   * 
   * @param user
   *          Name of the user, for which information about tracking him/her
   *          users are to be obtained, or <code>null</code> for currently
   *          authenticated user.
   * @return List of {@link Subscription} instances or <code>null</code> in case
   *         of error.
   */
  public abstract Iterable<? extends Subscription> getSubscriptionsTo(
      String user);

  /**
   * Gets the list of subscriptions <i>from</i> the given user.
   * 
   * @param user
   *          Name of the user, for which information about users tracked by
   *          him/her are to be obtained, or <code>null</code> for currently
   *          authenticated user.
   * @return List of {@link Subscription} instances or <code>null</code> in case
   *         of error.
   */
  public abstract Iterable<? extends Subscription> getSubscriptionsFrom(
      String user);

  /**
   * Deletes all subscriptions between the currently logged in user and a given
   * one.
   * 
   * @param user
   *          Name of a tracked user to delete from the subscriptions list.
   */
  public abstract void deleteSubscriptions(String user);

  /**
   * Changes the subscription information between the currently logged in user
   * and a given one.
   * 
   * @param user
   *          Name of a tracked user to update in the subscriptions list.
   * @param transport_www
   *          <code>true</code> if the user is to be tracked via the currently
   *          logged in user's dashboard.
   * @param transport_im
   *          <code>true</code> if the user is to be tracked via the currently
   *          logged in user's instant messaging client.
   */
  public abstract void setSubscription(String user, boolean transport_www,
      boolean transport_im);

  /**
   * Returns the user with specified ID number.
   * 
   * <p>
   * If user can't be obtained (for any reason), <code>null</code> is returned
   * and error message is set.
   * </p>
   * 
   * @param id
   *          Requested user's ID number.
   * @return <code>User</code> instance matching the request, or
   *         <code>null</code> in case of error.
   */
  public abstract User getUser(int id);

  /**
   * Returns the user with specified login name.
   * 
   * <p>
   * If login name is <code>null</code>, then the request asking for this
   * client's user is sent.
   * </p>
   * 
   * <p>
   * If user can't be obtained (for any reason), <code>null</code> is returned
   * and error message is set.
   * </p>
   * 
   * @param login
   *          Requested user's login name.
   * @return <code>User</code> instance matching the request, or
   *         <code>null</code> in case of error.
   */
  public abstract User getUser(String login);

  /**
   * Returns the error message generated by this client's operation.
   * 
   * <p>
   * This method should be used to obtain information about last operation. The
   * basic implementation of <code>ErrorMessage</code> provides only a message
   * string with short failure reason. You should feel free to subclass the
   * <code>ErrorMessage</code>, to make it suit your needs.
   * </p>
   * 
   * @return Error message containing information about client's last operation
   *         failure, or <code>null</code> if last operation execution did
   *         succeed.
   */
  public abstract ErrorMessage getLastError();

  /**
   * Returns version of the Blip API supported by this client.
   * 
   * @return {@link BlipApi} object representing the supported API version.
   */
  public abstract BlipApi getSupportedAPI();
}
